package com.pkche.core.advanced.reprint.extract;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import com.pkche.core.advanced.reprint.NetArticle;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.safety.Whitelist;
import org.jsoup.select.Elements;

/**
 * 抽象的文章抽取器 <br/>
 * Describe：提供统一的接口实现网络文章的抽取<br />
 * User："Qiang" <br/>
 * Time：2015年2月13日 下午1:27:25 <br/>
 */
public abstract class ArticleExtractor {
	// 当前的URL
	protected URL pageURL;
	/**
	 * 当前文章的网页DOM对象
	 */
	protected Document document;
	// 正文对象
	protected Element contextElement;
	// 标题
	private String title;
	// 关键词
	private String keywords;
	// 描述
	private String describe;
	// 是否包含视频
	private boolean isIncludeVideo;
	// 是否包含音频
	private boolean isIncludeAudio;

	// 默认的jsoup清除工具配置
	public static final Whitelist defaultWhite;

	static {
		defaultWhite = new Whitelist();
		// 为指定标记添加允许标签
		defaultWhite.addTags("img", "iframe", "br", "p");
		// 添加标签允许属性
		defaultWhite.addAttributes("img", "src", "data-src");
		defaultWhite.addAttributes("iframe", "src", "data-src", "width",
				"height");
		// 需要在此扩展来源视频检测

	}

	/**
	 * 构造一个抽取器
	 * 
	 * @param url
	 *            目标路径
	 * @param millis
	 *            提取文章允许的最大超时限制(毫秒)
	 * @throws java.io.IOException
	 */
	public ArticleExtractor(URL url, int millis) throws IOException {
		if (url == null) {
			return;
		}
		pageURL = url;
		// 拼接出一个属于当前主机的URL伪造来源
		String prevURL = url.getProtocol() + "://" + url.getHost();
		// 使用伪造的浏览器信息和来源，发送GET请求获取获取响应
		document = Jsoup
				.connect(url.toString())
				.header("User-Agent",
						"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2")
				.header("Referer", prevURL).timeout(millis).get();
		parseDocument();
		extractElement();
	}

	/**
	 * 构造一个抽取器
	 * 
	 * @param html
	 *            文章页面
	 */
	public ArticleExtractor(String html) {
		document = Jsoup.parse(html);
		parseDocument();
		extractElement();
	}

	/**
	 * 默认的空构造，只允许子类调用
	 */
	protected ArticleExtractor() {
	}

	/**
	 * 解析文档
	 */
	protected void parseDocument() {
		// 解析标题
		title = title();
		// 解析关键词
		Element ke = document.select("meta[name=Keywords]").first();
		keywords = (ke == null) ? null : ke.attr("content");
		// 解析简介
		Element de = document.select("meta[name=Description]").first();
		describe = (de == null) ? null : de.attr("content");

	}

	/**
	 * 抽取元素
	 */
	protected void extractElement() {
		contextElement = contextElement();
		isIncludeAudio = isIncludeAudio();
		isIncludeVideo = isIncludeMedia();
	}

	/**
	 * 解析并返回网页标题，这里默认获取到的是网页标题，即＜title＞＜/title＞中的内容，有些文章实现标题可能与网页标题存在差异，子类可酌情重写
	 * 
	 * @return
	 */
	public String title() {
		return document.title();
	}

	/**
	 * 解析并返回网页描述，子类可重写
	 * 
	 * @return
	 */
	public String describe() {
		return describe;
	}

	/**
	 * 解析并获取网页的关键词，子类可重写
	 * 
	 * @return
	 */
	public String keywrods() {
		return keywords;
	}

	/**
	 * 返回网页的DOM对象
	 * 
	 * @return
	 */
	public Document document() {
		return document;
	}

	/**
	 * 内容来源的URL，这里使用的是当前页面的URL，子类可根据解析结果进行重写
	 * 
	 * @return
	 */
	public URL sourceURL() {
		return pageURL;
	}

	/**
	 * 判断此内容是否包含视频，子类可重写判断方法
	 * 
	 * @return
	 */
	public abstract boolean isIncludeMedia();

	/**
	 * 判断此内容是否包含视频，子类可重写判断方法
	 * 
	 * @return
	 */
	public abstract boolean isIncludeAudio();

	/**
	 * 
	 * 因为各个内容源的文章页面DOM结构差异较大，需要针对不同的内容源构建不同的文章正文解析代码 <br/>
	 * 在这里定义一个获取文章正文DOM对象的方法，由上层抽象类调用完成一些通用的处理过程，如：解析正文中嵌套的图像，清洁HTML代码中存在的不安全标签等<br/>
	 * 
	 * @return 解析并返回文章正文DOM元素对象
	 */
	public abstract Element contextElement();

	/**
	 * 解析并返回网络文章的实体对象
	 * 
	 * @return
	 */
	public NetArticle get() {
		NetArticle e = new NetArticle();
		e.setTitle(title);
		e.setDescribe(describe);
		e.setKeywords(keywords);
		if (contextElement != null) {
			e.setContext(cleaner(contextElement.html()));
		}
		e.setUrl(pageURL);
		e.setImageList(imageList());
		// 默认为纯文字
		e.setContextType(NetArticle.ContextType.text);
		if (e.getImageList().size() > 0) {
			// 设置第一张图像
			e.setFristImage(e.getImageList().get(0));
			// 图文
			e.setContextType(NetArticle.ContextType.image_text);
		}
		if (isIncludeVideo) {
			// 视频
			e.setContextType(NetArticle.ContextType.video);
		} else if (this.isIncludeAudio) {
			// 音频
			e.setContextType(NetArticle.ContextType.audio);
		}
		return e;
	}

	/**
	 * 解析内容中包含的图像资源链接，子类可重写
	 * 
	 * @return
	 */
	public List<URL> imageList() {
		// <img src>
		Elements imgs = contextElement.select("img");
		List<URL> imgList = new ArrayList<URL>();
		// 遍历页面图像元素
		for (Element e : imgs) {
			String src = e.attr("src");
			if (src == null) {
				continue;
			}
			try {
				URL resource = new URL(src);
				imgList.add(resource);
			} catch (MalformedURLException e1) {
				// TODO Auto-generated catch block
				// e1.printStackTrace();
				// 不合法的URL丢弃
			}
		}
		return imgList;
	}

	/**
	 * 内容清洁器，用于清除内容中不需要的html代码，子类可重写
	 * 
	 * @param html
	 *            html源码
	 * @return 清洁后的源码
	 */
	public String cleaner(String html) {
		return Jsoup.clean(html, defaultWhite);
	}

	/**
	 * 内容来源
	 * 
	 * @return
	 */
	public abstract NetArticle.TargetType targetType();

}
